package com.wh1200.mybatis.xmlless.plugin

import com.wh1200.mybatis.xmlless.model.TotalPageRequest
import com.wh1200.mybatis.xmlless.util.XmllessInfoUtil
import com.wh1200.mybatis.xmlless.util.checkType
import com.baomidou.mybatisplus.annotation.DbType
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor
import com.baomidou.mybatisplus.extension.plugins.pagination.Page
import org.apache.ibatis.executor.Executor
import org.apache.ibatis.mapping.BoundSql
import org.apache.ibatis.mapping.MappedStatement
import org.apache.ibatis.session.ResultHandler
import org.apache.ibatis.session.RowBounds
import org.springframework.data.domain.Pageable

/**
 *
 * @author 吴昊
 * @date 2023/12/16 20:09
 * @since v4.0.0
 * @version 1.0
 */

class SpringPagePlugin(dbType: DbType) : PaginationInnerInterceptor(dbType) {

  /**
   * 对于未经过xmlless解析的分页方法，处理返回值
   */
  override fun willDoQuery(
      executor: Executor,
      ms: MappedStatement,
      parameter: Any?,
      rowBounds: RowBounds,
      resultHandler: ResultHandler<*>?,
      boundSql: BoundSql
  ): Boolean {
    // 获取spring data的Pageable分页参数
    val pageable = getPageableParameter(parameter)

    // 过滤被xmlless解析的方法
    if (XmllessInfoUtil.isXmllessResolvedMethod(ms.id) || pageable == null || !pageable.isPaged || pageable.pageNumber < 0) {
      return super.willDoQuery(
          executor, ms, parameter,
          rowBounds, resultHandler, boundSql
      )
    }

    // 在之前的方法中将Pageable对象替换为了TotalPageRequest对象，否则这里获取到的总数无法传递
    if (pageable !is TotalPageRequest) {
      return super.willDoQuery(executor, ms, parameter, rowBounds, resultHandler, boundSql)
    }

    // important: 当分页参数为Pageable对象时，并使用了xml注入，要时mybatis-plus的分页生效，参数列表中必须要有IPage类型的参数
    val page = Page<Any>(pageable.pageNumber.toLong(), pageable.pageSize.toLong())
    if (parameter.checkType<MutableMap<String, Any?>>()) {
      parameter["_page_"] = page
    }
    val result = super.willDoQuery(executor, ms, parameter, rowBounds, resultHandler, boundSql)
    pageable.setTotal(page.total)
    return result
  }

  private fun getPageableParameter(parameter: Any?): Pageable? {
    if (parameter is Map<*, *>) {
      parameter.entries.forEach {
        if (it.value is Pageable) {
          return it.value as Pageable
        }
      }
    } else if (parameter is Pageable) {
      return parameter
    }
    return null
  }

}
